package com.dingwen.treasure.mongodb.controller;

import cn.hutool.core.convert.Convert;
import com.dingwen.treasure.base.controller.BaseCrudController;
import com.dingwen.treasure.base.convert.model.impl.AbstractBeanConvertImpl;
import com.dingwen.treasure.base.exception.BaseCrudException;
import com.dingwen.treasure.base.util.ReflectUtil;
import com.dingwen.treasure.base.pojo.vo.Result;
import com.dingwen.treasure.base.pojo.vo.ResultGenerator;
import com.dingwen.treasure.mongodb.service.MongoBaseService;
import com.dingwen.treasure.mongodb.vo.MongoPageVo;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.mongodb.client.result.DeleteResult;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 　AbstractBaseControllerMongoTemplateImpl
 * 基础 crud 封装 controller mongo template 实现 </br>
 * <p>泛型说明：</p>
 * <ul>
 *     <li>P： po 持久化对象</li>
 *     <li>D: dto 数据传输对象</li>
 *     <li>V： vo 视图对象</li>
 * </ul>
 *
 * <p> 优化记录： </p>
 * <ul>
 *     <li> getDTO（）优化：多线程条件下加同步锁较少调用次数,只是获取值，修改为 volatile 一旦赋值多线程立刻可见 </li>
 * </ul>
 * <p>
 * 　@author dingwen
 * 　@date 2022/6/30
 */
@Slf4j
public abstract class AbstractBaseCrudControllerMongoTemplateImpl
        <
                P extends Serializable, // 实体
                D extends AbstractBeanConvertImpl<P, D, V>, // 数据传输对象
                V // 视图对象
                > implements BaseCrudController<D> {

    /**
     * 数据传输对象
     */
     protected volatile D dto;

    /**
     * mongoBaseService
     */
    @Autowired
    protected MongoBaseService<P> mongoBaseService;


    /**
     * 默认关闭 BO 到 VO 的转换
     *
     * @return {@link Boolean}
     */
    @Override
    public Boolean enableBoToVo() {
        return Boolean.FALSE;
    }

    /* --------------------------------------------搜索--------------------------------------------*/

    /**
     * 搜索所有符合条件的列表 </br>
     * <pre>
     *     1. MongoDB 暂不支持后期完善 TODO
     * </pre>
     *
     * @param params 搜索参数
     * @return {@link Result}
     */
    @Override
    public Result<?> searchAll(Map<String, Object> params) {
        throw new BaseCrudException("MongoDB 暂不支持搜索所有符合条件的列表，请自行实现");
    }

    /**
     * 分页搜索所有符合条件的列表（支持单表、多表联查）
     *
     * @param params 参数
     * @return {@link Result}
     */
    @Override
    public Result<?> search(Map<String, Object> params) {
        throw new BaseCrudException("MongoDB暂不支持分页搜索所有符合条件的列表（支持单表、多表联查），请自行实现");
    }

    /**
     * 搜索符合条件的第一条记录
     *
     * @param params 参数
     * @return {@link Result}
     */
    @Override
    public Result<?> searchFirst(Map<String, Object> params) {
        throw new BaseCrudException("MongoDB暂不支持分页搜索所有符合条件的第一条记录（支持单表、多表联查），请自行实现");
    }

    /* --------------------------------------------查询--------------------------------------------*/

    /**
     * 根据Id查询，返回单个实体
     *
     * @param id 数据表主键
     * @return {@link Result}
     */
    @Override
    @ApiOperation("根据主键查询,返回单个对象")
    @ApiOperationSupport(author = "dingwen")
    @ApiImplicitParam(value = "主键", name = "id")
    @GetMapping("/{id}")
    public Result<?> findById(@PathVariable(value = "id") Serializable id) {

        // 数据库对象
        P po = mongoBaseService.findById(id);
        // 数据传输对象
        D dto = getDto();
        if (enableBoToVo()) {
            // 需要进行转换
            // 视图对象
            V vo = dto.poToVo(po);
            return ResultGenerator.genSuccessResult(vo);
        }
        return ResultGenerator.genSuccessResult(po);
    }

    /**
     * 根据List<id>查找实体
     *
     * @param ids id 传参示例： 1,2,3 集合长度至少为2
     * @return {@link Result}
     */
    @Override
    @ApiOperation("根据主键集合查询，返回对象 List")
    @ApiOperationSupport(author = "dingwen")
    @ApiImplicitParam(value = "主键List集合", name = "ids", dataType = "body")
    @PostMapping("/ids")
    public Result<?> findByIds(@RequestBody List<Serializable> ids) {
        //数据库对象
        List<P> pos = mongoBaseService.findByIds(ids);
        if (enableBoToVo()) {
            // 页面对象
            List<V> vos = pos.parallelStream()
                    .map(po -> getDto().poToVo(po))
                    .collect(Collectors.toList());
            return ResultGenerator.genSuccessResult(vos);
        }
        return ResultGenerator.genSuccessResult(pos);
    }

    /**
     * 无条件查询所有
     *
     * @return {@link Result}
     */
    @Override
    @ApiOperation("查询所有，返回对象 List")
    @ApiOperationSupport(author = "dingwen")
    @GetMapping("list")
    public Result<?> findAll() {
        // 数据库对象
        List<P> pos = mongoBaseService.findAll();
        if (enableBoToVo()) {
            // 页面对象
            List<V> vos = pos.parallelStream()
                    .map(po -> getDto().poToVo(po))
                    .collect(Collectors.toList());
            return ResultGenerator.genSuccessResult(vos);
        }
        return ResultGenerator.genSuccessResult(pos);
    }

    /**
     * 无条件分页查询
     *
     * @param pageSize 页面大小
     * @param pageNum  页面num
     * @return {@link Result}
     */
    @Override
    @ApiOperation("分页查询,返回 对象 page")
    @ApiOperationSupport(author = "dingwen")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "pageSize", value = "页面大小"),
                    @ApiImplicitParam(name = "pageNum", value = "页码数")
            }
    )
    @GetMapping("/page")
    public Result<MongoPageVo> page(@RequestParam(name = "pageSize", required = false, defaultValue = "10") Integer pageSize,
                                    @RequestParam(name = "pageNum", required = false, defaultValue = "1") Integer pageNum) {
        final MongoPageVo<P> page = mongoBaseService.findPage(pageSize, pageNum);
        if (enableBoToVo()) {
            List<V> vos = page.getData()
                    .parallelStream()
                    .map(po -> getDto().poToVo(po)).collect(Collectors.toList());
            page.setData((List<P>) vos);
        }
        return ResultGenerator.genSuccessResult(page);
    }

    /**
     * 获取数据表总记录条数
     *
     * @return {@link Result}
     */
    @Override
    @ApiOperation("获取数据表总记录条数，返回统计数量")
    @ApiOperationSupport(author = "dingwen")
    @GetMapping("/count")
    public Result<Long> count() {
        return ResultGenerator.genSuccessResult(mongoBaseService.count());
    }

    /**
     * 通过 id 查询是否存在 </br>
     * <ol>
     *     <li>true: 存在</li>
     *     <li>false: 不存在</li>
     * </ol>
     *
     * @param id 主键id
     * @return {@link Result}
     */
    @Override
    @ApiOperation("根据对象 id 查询对象是否存在,返回布尔值")
    @ApiOperationSupport(author = "dingwen")
    @GetMapping("/exists/{id}")
    public Result<Boolean> existsById(@PathVariable("id") Serializable id) {
        return ResultGenerator.genSuccessResult(mongoBaseService.existsById(id));
    }


    /* --------------------------------------------新增--------------------------------------------*/

    /**
     * 保存
     *
     * @param dto 数据传输对象
     * @return {@link Result}
     */
    @Override
    @ApiOperation("保存一个对象到数据库，返回布尔值")
    @ApiOperationSupport(author = "dingwen")
    @ApiImplicitParam(value = "数据传输对象", name = "dto", dataType = "body")
    @PostMapping
    public Result<P> save(@RequestBody D dto) {
        return ResultGenerator.genSuccessResult(mongoBaseService.sava(dto.dtoToPo(dto)));

    }

    /**
     * 批量保存
     *
     * @param dtos 数据传输对象列表
     * @return {@link Result}
     */
    @Override
    @ApiOperation("批量添加，返回布尔值")
    @ApiOperationSupport(author = "dingwen")
    @ApiImplicitParam(name = "ts", value = "条件集合Json对象")
    @PostMapping("/batch")
    public Result<?> batchSave(@RequestBody List<D> dtos) {
        List<P> pos = dtos.parallelStream().map(dto -> dto.dtoToPo(dto)).collect(Collectors.toList());
        return ResultGenerator.genSuccessResult(mongoBaseService.saveBatch(pos));

    }


    /* --------------------------------------------删除--------------------------------------------*/

    /**
     * 根据ID删除
     *
     * @param id id
     * @return {@link Result}
     */
    @Override
    @ApiOperation("删除单条记录，返回布尔值")
    @ApiOperationSupport(author = "dingwen")
    @ApiImplicitParam(name = "id", value = "主键")
    @DeleteMapping("/{id}")
    public Result<?> removeById(@PathVariable(value = "id") Serializable id) {
        return ResultGenerator.genSuccessResult(mongoBaseService.removeById(Convert.toStr(id)));
    }

    /**
     * 根据主键集合批量删除
     *
     * @param ids id
     * @return {@link Result}
     */
    @Override
    @ApiOperation("批量删除记录,返回布尔值")
    @ApiOperationSupport(author = "dingwen")
    @ApiImplicitParam(name = "ids", value = "主键集合")
    @DeleteMapping("/batch")
    public Result<DeleteResult> batchRemoveByIds(List<Serializable> ids) {
        return ResultGenerator.genSuccessResult(mongoBaseService.removeByIds(ids));
    }



    /* --------------------------------------------修改--------------------------------------------*/

    /**
     * 修改,对象 id ,主键必须有值,默认获取 t 中的 id 属性
     *
     * @param dto 数据传输对象
     * @return {@link Result}
     */
    @Override
    public Result modify(D dto) {
        return null;
    }

    /**
     * 修改
     * 修改,对象 id ,主键必须有值
     *
     * @param dto            数据传输对象             t
     * @param fieldNameForId 指定Id 字段名称
     * @param id  主键id（符合rest接口定义规范）
     * @return {@link Result}
     */
    @Override
    public Result modify(D dto, Serializable id,String fieldNameForId) {
        return null;
    }

    /**
     * 修改
     * 修改,批量修改,对象主键必须要有值,,默认获取 t 中的 id 属性
     *
     * @param dtos           数据传输对象列表
     * @param fieldNameForId 指定Id 字段名称
     * @return {@link Result}
     */
    @Override
    public Result batchModify(List<D> dtos, String fieldNameForId) {
        return null;
    }

    /**
     * 修改,批量修改,对象主键必须要有值
     *
     * @param dtos 数据传输对象列表
     * @return {@link Result}
     */
    @Override
    public Result batchModify(List<D> dtos) {
        return null;
    }


    /**
     * 获取dto
     *
     * @return {@link D} dto 数据传输对象
     */
    @SneakyThrows(Throwable.class)
    public D getDto() {
        if (!Objects.isNull(dto)) {
            return dto;
        }
        Class<D> dtoClass = ReflectUtil.getClassForGeneric(1, this);
        dto = dtoClass.newInstance();
        return dto;
    }
}
